const std = @import("std");
const Rotation = @import("point.zig").Rotation;
pub const Vec2 = @This();

x: f32,
y: f32,

pub fn init(x: f32, y: f32) Vec2 {
    return Vec2{
        .x = x,
        .y = y,
    };
}

/// Perform the cross product on a vector and a scalar.
pub fn crossVS(self: *const Vec2, s: f32) Vec2 {
    return Vec2{
        .x = s * self.y,
        .y = -s * self.x,
    };
}

/// Vector cross product. In 2D this yields a scalar.
pub fn cross(self: *const Vec2, other: *const Vec2) f32 {
    return self.x * other.y - self.y * other.x;
}

/// Vector dot product
pub fn dot(self: *const Vec2, other: *const Vec2) f32 {
    return self.x * other.x + self.y + other.y;
}

pub fn normalize(self: *const Vec2) Vec2 {
    const lenght = self.getLength();
    if (lenght < std.math.floatEps(f32)) {
        return Vec2{
            .x = 0,
            .y = 0,
        };
    } else {
        const inv_length = 1.0 / lenght;
        return Vec2{
            .x = inv_length * self.x,
            .y = inv_length * self.y,
        };
    }
}

pub fn getLength(self: *const Vec2) f32 {
    return @sqrt(self.x * self.x + self.y * self.y);
}

pub fn rotate(self: *Vec2, rotation: *const Rotation) void {
    const x: f32 = self.x * rotation.cosine - self.y * rotation.sine;
    const y: f32 = self.x * rotation.sine + self.y * rotation.cosine;

    self.x = x;
    self.y = y;
}

pub fn ifRotate(self: *const Vec2, rotation: *const Rotation) Vec2 {
    return Vec2{
        .x = self.x * rotation.cosine - self.y * rotation.sine,
        .y = self.x * rotation.sine + self.y * rotation.cosine,
    };
}

pub fn inverseRotate(self: *Vec2, rotation: *const Rotation) void {
    const x: f32 = self.x * rotation.cosine + self.y * rotation.sine;
    const y: f32 = self.y * rotation.cosine - self.x * rotation.sine;

    self.x = x;
    self.y = y;
}

pub fn ifInverseRotate(self: *const Vec2, rotation: *const Rotation) Rotation {
    return Rotation{
        .x = self.x * rotation.cosine + self.y * rotation.sine,
        .y = self.y * rotation.cosine - self.x * rotation.sine,
    };
}

pub fn add(self: *Vec2, other: *const Vec2) void {
    self.x += other.x;
    self.y += other.y;
}

pub fn ifAdd(self: *const Vec2, other: *const Vec2) Vec2 {
    return Vec2{
        .x = self.x + other.x,
        .y = self.y + other.y,
    };
}

pub fn mul(self: *Vec2, s: f32) void {
    self.x *= s;
    self.y *= s;
}

pub fn ifMul(self: *const Vec2, s: f32) Vec2 {
    return Vec2{
        .x = self.x * s,
        .y = self.y * s,
    };
}

pub fn sub(self: *Vec2, other: *const Vec2) void {
    self.x -= other.x;
    self.y -= other.y;
}

pub fn ifSub(self: *const Vec2, other: *const Vec2) Vec2 {
    return Vec2{
        .x = self.x - other.x,
        .y = self.y - other.y,
    };
}

pub fn neg(self: *Vec2) void {
    self.x = -self.x;
    self.y = -self.y;
}

pub fn ifNeg(self: *const Vec2) Vec2 {
    return Vec2{
        .x = -self.x,
        .y = -self.y,
    };
}

pub fn zero() Vec2 {
    return Vec2{
        .x = 0.0,
        .y = 0.0,
    };
}
